package com.example.musicplayer.gobang;

public class AI_2 {
    private final int SIZE;
    private final Gobang mGobang;
    private final Location[][] mLoc;
    private final int[][] mScore;

    public AI_2(Gobang gobang) {
        mGobang = gobang;
        SIZE = mGobang.getChessBoardSize();
        mLoc = new Location[SIZE][SIZE];
        mScore = new int[SIZE][SIZE];
    }

    public static void calculateScore(int[][] data, int[][] score, int size) {
        for (int i = 0; i < size; i++) {
            for (int j = 0; j < size; j++) {
                score[i][j] = -1;
            }
        }
        Location[][] location = new Location[size][size];
        updateLoc(data, size, location);
        updateScore(data, size, location, score);
    }

    public Location nextLocation() {
        for (int i = 0; i < SIZE; i++) {
            for (int j = 0; j < SIZE; j++) {
                mScore[i][j] = -1;
            }
        }
        updateLoc(mGobang.copyChessLocation(), SIZE, mLoc);
        updateScore(mGobang.copyChessLocation(), SIZE, mLoc, mScore);
        int maxScore = -1;
        int x = 0;
        int y = 0;
        for (int i = 0; i < SIZE; i++) {
            for (int j = 0; j < SIZE; j++) {
                if (mScore[i][j] > maxScore) {
                    maxScore = mScore[i][j];
                    x = i;
                    y = j;
                }
            }
        }
        return new Location(x, y);
    }

    //calculate each tuple five
    private static void updateLoc(int[][] data, int size, Location[][] loc) {
        for (int i = 0; i < size; i++) {
            for (int j = 0; j < size; j++) {
                Location location = new Location(i, j);
                location.setTupleH(scoreTupleH(data, size, i, j));
                location.setTupleV(scoreTupleV(data, size, i, j));
                location.setTupleFS(scoreTupleFS(data, size, i, j));
                location.setTupleBS(scoreTupleBS(data, size, i, j));
                loc[i][j] = location;
            }
        }
    }

    //horizontal, to right
    private static int scoreTupleH(int[][] data, int size, int x, int y) {
        if (y + 4 < size) {
            int num_black = 0;
            int num_white = 0;
            for (int k = 0; k <= 4; k++) {
                if (data[x][y + k] == Gobang.BLACK) {
                    num_black++;
                }
                if (data[x][y + k] == Gobang.WHITE) {
                    num_white++;
                }
            }
            return ScoreTable.getScore(num_black, num_white);
        }
        return 0;
    }

    //vertical, down
    private static int scoreTupleV(int[][] data, int size, int x, int y) {
        if (x + 4 < size) {
            int num_black = 0;
            int num_white = 0;
            for (int k = 0; k <= 4; k++) {
                if (data[x + k][y] == Gobang.BLACK) {
                    num_black++;
                }
                if (data[x + k][y] == Gobang.WHITE) {
                    num_white++;
                }
            }
            return ScoreTable.getScore(num_black, num_white);
        }
        return 0;
    }

    //forward slash, left-down
    private static int scoreTupleFS(int[][] data, int size, int x, int y) {
        if (x + 4 < size && y - 4 >= 0) {
            int num_black = 0;
            int num_white = 0;
            for (int k = 0; k <= 4; k++) {
                if (data[x + k][y - k] == Gobang.BLACK) {
                    num_black++;
                }
                if (data[x + k][y - k] == Gobang.WHITE) {
                    num_white++;
                }
            }
            return ScoreTable.getScore(num_black, num_white);
        }
        return 0;
    }

    //back slash, right-down
    private static int scoreTupleBS(int[][] data, int size, int x, int y) {
        if (x + 4 < size && y + 4 < size) {
            int num_black = 0;
            int num_white = 0;
            for (int k = 0; k <= 4; k++) {
                if (data[x + k][y + k] == Gobang.BLACK) {
                    num_black++;
                }
                if (data[x + k][y + k] == Gobang.WHITE) {
                    num_white++;
                }
            }
            return ScoreTable.getScore(num_black, num_white);
        }
        return 0;
    }

    private static void updateScore(int[][] data, int size, Location[][] loc, int[][] score) {
        for (int i = 0; i < size; i++) {
            for (int j = 0; j < size; j++) {
                if (data[i][j] == Gobang.EMPTY) {
                    score[i][j] = scoreH(loc, i, j) + scoreV(loc, i, j) + scoreFS(loc, i, j) + scoreBS(loc, i, j);
                }
            }
        }
    }

    //calculate all score h
    private static int scoreH(Location[][] loc, int x, int y) {
        int score = 0;
        for (int k = 0; k <= 4; k++) {
            if (y - k >= 0) {
                score += loc[x][y - k].getTupleH();
            }
        }
        return score;
    }

    //calculate all score v
    private static int scoreV(Location[][] loc, int x, int y) {
        int score = 0;
        for (int k = 0; k <= 4; k++) {
            if (x - k >= 0) {
                score += loc[x - k][y].getTupleV();
            }
        }
        return score;
    }

    //calculate all score fs
    private static int scoreFS(Location[][] loc, int x, int y) {
        int score = 0;
        for (int k = 0; k <= 4; k++) {
            if (x - k >= 0 && y + k < loc.length) {
                score += loc[x - k][y + k].getTupleFS();
            }
        }
        return score;
    }

    //calculate all score bs
    private static int scoreBS(Location[][] loc, int x, int y) {
        int score = 0;
        for (int k = 0; k <= 4; k++) {
            if (x - k >= 0 && y - k >= 0) {
                score += loc[x - k][y - k].getTupleBS();
            }
        }
        return score;
    }

    private static class ScoreTable {
        //get the tuple five score
        public static final int BLANK = 7;
        public static final int POLLUTED = 0;
        //only white
        public static final int W = 15;
        public static final int WW = 400;
        public static final int WWW = 1800;
        public static final int WWWW = 100000;
        //only black
        public static final int B = 35;
        public static final int BB = 800;
        public static final int BBB = 15000;
        public static final int BBBB = 800000;

        /**
         * return the score of tuple five
         *
         * @param num_black num of black chess
         * @param num_white num of white chess
         * @return the score
         */
        public static int getScore(int num_black, int num_white) {
            if (num_black == 0 && num_white == 0) {
                return BLANK;
            } else if (num_black > 0 && num_white > 0) {
                return POLLUTED;
            } else if (num_black > 0) {
                switch (num_black) {
                    case 1:
                        return B;
                    case 2:
                        return BB;
                    case 3:
                        return BBB;
                    case 4:
                    default:
                        return BBBB;
                }
            } else {
                switch (num_white) {
                    case 1:
                        return W;
                    case 2:
                        return WW;
                    case 3:
                        return WWW;
                    case 4:
                    default:
                        return WWWW;
                }
            }
        }
    }
}